# Copyright (c) 2024, kaepyz@gmail.com and contributors
# For license information, please see license.txt

import requests
import hashlib
import time
import json
from typing import Dict, Optional, Any
import frappe
from frappe import _


class TongToolAPIError(Exception):
    """通途API异常"""
    def __init__(self, code: int, message: str):
        self.code = code
        self.message = message
        super().__init__(f"通途API错误 {code}: {message}")


class TongToolClient:
    """通途API客户端"""
    
    # 错误码映射
    ERROR_CODE_MAPPING = {
        519: "签名错误",
        523: "Token已过期", 
        524: "未授权的请求",
        525: "无效的参数",
        526: "接口请求超频",
        527: "系统错误",
        999999: "账号已过期"
    }
    
    def __init__(self, app_key: str, app_secret: str, base_url: str = None):
        self.app_key = app_key
        self.app_secret = app_secret
        self.base_url = base_url or "https://open.tongtool.com/api-service"
        self.auth_url = "https://open.tongtool.com/open-platform-service"
        self.session = requests.Session()
        self.token_info = None
        
        # 配置会话
        self.session.headers.update({
            'Content-Type': 'application/json',
            'Accept': 'application/json',
            'api_version': '3.0'
        })
        
        # 获取配置 - 使用默认值避免初始化时的循环依赖
        try:
            settings = frappe.get_single('Tongtool Settings')
            self.cache_enabled = getattr(settings, 'enable_cache', True)
            self.cache_ttl = getattr(settings, 'cache_ttl_minutes', 10) * 60
            self.max_retries = getattr(settings, 'max_retry_attempts', 3)
            self.retry_delay = getattr(settings, 'retry_delay_minutes', 5) * 60
        except:
            # 如果无法获取设置（比如在初始化时），使用默认值
            self.cache_enabled = True
            self.cache_ttl = 10 * 60
            self.max_retries = 3
            self.retry_delay = 5 * 60
        
    def authenticate(self) -> bool:
        """获取token和商户ID"""
        try:
            # 1. 获取app_token
            response = self.session.get(
                f"{self.auth_url}/devApp/appToken",
                params={
                    'accessKey': self.app_key,
                    'secretAccessKey': self.app_secret
                },
                timeout=30
            )
            response.raise_for_status()
            
            auth_result = response.json()
            if not auth_result.get('success'):
                raise TongToolAPIError(
                    auth_result.get('code', 0), 
                    auth_result.get('message', 'Authentication failed')
                )
            
            app_token = auth_result['datas']
            
            # 2. 获取商户信息
            timestamp = int(time.time())
            sign = hashlib.md5(
                f"app_token{app_token}timestamp{timestamp}{self.app_secret}".encode()
            ).hexdigest()
            
            merchant_response = self.session.get(
                f"{self.auth_url}/partnerOpenInfo/getAppBuyerList",
                params={
                    'app_token': app_token,
                    'timestamp': timestamp,
                    'sign': sign
                },
                timeout=30
            )
            merchant_response.raise_for_status()
            
            merchant_result = merchant_response.json()
            if not merchant_result.get('success') or not merchant_result.get('datas'):
                raise TongToolAPIError(
                    merchant_result.get('code', 0),
                    merchant_result.get('message', 'No merchant data found')
                )
            
            merchants = merchant_result['datas']
            if not merchants:
                raise TongToolAPIError(0, "未找到商户信息")
            
            # 保存token信息
            self.token_info = {
                'app_token': app_token,
                'merchant_id': merchants[0]['partnerOpenId'],
                'timestamp': timestamp,
                'sign': sign,
                'expire_time': time.time() + 3600  # 1小时后过期
            }
            
            frappe.logger().info(f"通途API认证成功，商户ID: {self.token_info['merchant_id']}")
            return True
            
        except requests.exceptions.RequestException as e:
            frappe.log_error(f"通途API认证网络错误: {str(e)}")
            return False
        except Exception as e:
            frappe.log_error(f"通途API认证失败: {str(e)}")
            return False
    
    def _is_token_expired(self) -> bool:
        """检查token是否过期"""
        if not self.token_info:
            return True
        return time.time() > self.token_info['expire_time']
    
    def _refresh_token_if_needed(self) -> bool:
        """如果需要则刷新token"""
        if self._is_token_expired():
            return self.authenticate()
        return True
    
    def _handle_rate_limit(self, attempt: int) -> None:
        """处理API频率限制"""
        if attempt < self.max_retries:
            wait_time = 60  # 等待1分钟
            frappe.logger().info(f"通途API请求超频，等待{wait_time}秒后重试")
            time.sleep(wait_time)
    
    def request(self, method: str, endpoint: str, data: Dict = None, params: Dict = None) -> Dict:
        """统一请求方法，包含重试和错误处理"""
        
        # 确保token有效
        if not self._refresh_token_if_needed():
            raise TongToolAPIError(0, "无法获取有效的认证token")
        
        # 准备请求参数
        request_params = {
            'app_token': self.token_info['app_token'],
            'sign': self.token_info['sign'],
            'timestamp': str(self.token_info['timestamp'])
        }
        
        if params:
            request_params.update(params)
        
        url = f"{self.base_url}{endpoint}"
        
        # 重试机制
        last_exception = None
        
        for attempt in range(self.max_retries):
            try:
                # 准备请求数据
                request_data = data.copy() if data else {}
                if method.upper() == 'POST' and request_data is not None:
                    request_data['merchantId'] = self.token_info['merchant_id']
                
                # 发送请求
                if method.upper() == 'POST':
                    response = self.session.post(
                        url, 
                        params=request_params, 
                        json=request_data, 
                        timeout=60
                    )
                else:
                    response = self.session.get(
                        url, 
                        params={**request_params, **(request_data or {})}, 
                        timeout=60
                    )
                
                response.raise_for_status()
                result = response.json()
                
                # 检查业务错误
                code = result.get('code')
                message = result.get('message', '')
                
                if code != 200:  # 通途API成功码是200
                    if code == 526:  # 请求超频
                        self._handle_rate_limit(attempt)
                        continue
                    
                    error_msg = self.ERROR_CODE_MAPPING.get(code, message or "未知错误")
                    raise TongToolAPIError(code, error_msg)
                
                return result
                
            except TongToolAPIError:
                # 业务错误直接抛出，不重试
                raise
                
            except requests.exceptions.RequestException as e:
                last_exception = e
                if attempt < self.max_retries - 1:
                    wait_time = 2 ** attempt  # 指数退避
                    frappe.logger().info(f"请求失败，{wait_time}秒后重试: {str(e)}")
                    time.sleep(wait_time)
                continue
                
            except Exception as e:
                last_exception = e
                frappe.log_error(f"通途API请求异常: {str(e)}")
                break
        
        # 所有重试都失败
        error_msg = f"请求失败，已重试{self.max_retries}次: {str(last_exception)}"
        raise TongToolAPIError(0, error_msg)
    
    def get_cache_key(self, key_data: Any) -> str:
        """生成缓存键"""
        if isinstance(key_data, dict):
            key_str = json.dumps(key_data, sort_keys=True)
        else:
            key_str = str(key_data)
        
        cache_key = hashlib.md5(key_str.encode()).hexdigest()
        return f"tongtool_{cache_key}"
    
    def get_cached_data(self, cache_key: str) -> Optional[Any]:
        """获取缓存数据"""
        if not self.cache_enabled:
            return None
        
        try:
            cached_data = frappe.cache().get_value(cache_key)
            if cached_data:
                frappe.logger().debug(f"使用缓存数据: {cache_key}")
                return cached_data
        except Exception as e:
            frappe.logger().debug(f"获取缓存失败: {str(e)}")
        
        return None
    
    def set_cached_data(self, cache_key: str, data: Any) -> None:
        """设置缓存数据"""
        if not self.cache_enabled:
            return
        
        try:
            frappe.cache().set_value(cache_key, data, expires_in_sec=self.cache_ttl)
            frappe.logger().debug(f"数据已缓存: {cache_key}")
        except Exception as e:
            frappe.logger().debug(f"设置缓存失败: {str(e)}")


def get_tongtool_client() -> TongToolClient:
    """获取通途客户端实例"""
    settings = frappe.get_single('Tongtool Settings')
    
    if not settings.app_key:
        frappe.throw("请先在通途设置中配置App Key")
    
    # 获取真实的密码值（ERPNext的Password字段读取时会被掩码）
    app_secret = settings.get_password('app_secret')
    if not app_secret:
        frappe.throw("请先在通途设置中配置App Secret")
    
    return TongToolClient(
        app_key=settings.app_key,
        app_secret=app_secret,
        base_url=settings.api_base_url
    )
